#include "game.h"

#include <assert.h>
#include <string.h>

#include <anet/util.h>

#include "client.h"
#include "role.h"

#define HERO_HP               100
#define HERO_MP               30
#define HERO_HP_RECOVER       0.1f
#define HERO_MP_RECOVER       0.5f
#define HERO_MP_CONSUMPTION   10
#define HERO_ATTACK           1
#define HERO_CRITICAL_MULTIPY 1.2f
#define HERO_CRITICAL_RATE    0.1f

#define SWORD_HP              100
#define SWORD_MP              30
#define SWORD_HP_RECOVER      0.1f
#define SWORD_MP_RECOVER      0.5f
#define SWORD_MP_CONSUMPTION  10
#define SWORD_ATTACK          19
#define SWORD_DAMAGE          10

#define BOSS_HP               100
#define BOSS_MP               30
#define BOSS_HP_RECOVER       0.1f
#define BOSS_MP_RECOVER       0.5f
#define BOSS_MP_CONSUMPTION   10
#define BOSS_ATTACK           15
#define BOSS_CRITICAL_MULTIPY 1.2f
#define BOSS_CRITICAL_RATE    0.1f
#define BOSS_DEFENSE_MULTIPY  0.8f


Game* GameCreate(const char* name) {
    Game* game = (Game *)malloc(sizeof(Game));
    game->name = strdup(name);
    game->hero   = NULL;
    game->sword  = NULL;
    game->boss   = NULL;
    game->status = GAME_IDLE;
    return game;
}

void GameDestroy(void* game) {
    if (game) {
        Game* g = (Game *)game;
        free(g->name);
        free(g);
    }
}

int GameJoinIn(Game* game, struct Client* client) {
  if (game->hero == NULL) {
    Role* role = RoleCreate(game->name);    
    role->who    = ROLE_HERO;
    role->target = HeroCreate(HERO_HP, HERO_HP_RECOVER, HERO_MP, HERO_MP_RECOVER,
        HERO_MP_CONSUMPTION, HERO_ATTACK, HERO_CRITICAL_MULTIPY, HERO_CRITICAL_RATE);
    client->data = role;
    game->hero   = client;
    if (game->hero && game->boss && game->sword) {
      game->status = GAME_RUNNING;
    }
    return 0;
  }
  if (game->boss == NULL) {
    Role* role = RoleCreate(game->name);    
    role->who    = ROLE_BOSS;
    role->target = BossCreate(BOSS_HP, BOSS_HP_RECOVER, BOSS_MP, BOSS_MP_RECOVER,
        BOSS_MP_CONSUMPTION, BOSS_ATTACK, BOSS_CRITICAL_MULTIPY, BOSS_CRITICAL_RATE);
    client->data = role;
    game->boss   = client;
    if (game->hero && game->boss && game->sword) {
      game->status = GAME_RUNNING;
    }
    return 0;
  }
  if (game->sword == NULL) {
    Role* role = RoleCreate(game->name);    
    role->who    = ROLE_SWORD;
    role->target = SwordCreate(SWORD_HP, SWORD_HP_RECOVER, SWORD_MP, SWORD_MP_RECOVER,
        SWORD_MP_CONSUMPTION, SWORD_ATTACK, SWORD_DAMAGE);
    client->data = role;
    game->sword  = client;
    if (game->hero && game->boss && game->sword) {
      game->status = GAME_RUNNING;
    }
    return 0;
  }

  return -1;
}

int GameIsPreparedToJudge(Game* game) {
    if (game->hero) {
        Role* role = (Role *)game->hero->data;
        Hero* hero = (Hero *)role->target;
        if (hero->status == STATUS_IDLE) return 0;
    }
    if (game->sword) {
        Role* role = (Role *)game->sword->data;
        Sword* sword = (Sword *)role->target;
        if (sword->status == STATUS_IDLE) return 0;
    }
    if (game->boss) {
        Role* role = (Role *)game->boss->data;
        Boss* boss = (Boss *)role->target;
        if (boss->status == STATUS_IDLE) return 0;
    }
    return 1;
}

int GameProceed(Game* game) {
    if (game->status != GAME_RUNNING) return 0;
    LogInfo("game proceed");

    Boss* boss = NULL;
    {
        Role* role = (Role *)game->boss->data;
        boss = (Boss *)role->target; 
    }
    Sword* sword = NULL;
    {
        Role* role = (Role *)game->sword->data;
        sword = (Sword *)role->target;
    }
    Hero* hero = NULL;
    {
        Role* role = (Role *)game->hero->data;
        hero = (Hero *)role->target;
    }

    switch (boss->status) {
        case STATUS_ATTACK: BossAttack(boss, hero, sword); break;
        case STATUS_DEFEND: BossDefend(boss); break;
        default: BossPass(boss);
    }

    switch (sword->status) {
        case STATUS_DODGE: SwordDodge(sword, boss); break;
        default: SwordPass(sword);
    }

    switch (hero->status) {
        case STATUS_ATTACK: HeroAttack(hero, sword, boss); break; 
        case STATUS_CRITICAL_ATTACK: HeroCriticalAttack(hero, sword, boss); break;
        default: HeroPass(hero);
    }

    if (hero->hp <= 0 || sword->hp <= 0 || boss->hp <= 0) {
        game->status = GAME_OVER;
    }
    return 0;
}

int GameToBuffer(Game* game, char** b) {
    if (game == NULL) {
        *b = (char *)malloc(sizeof(char) * 4);
        *(int *)(*b) = GAME_OVER;
        return 4;
    }

    int namelen = strlen(game->name);
    int length = namelen + sizeof(int) + sizeof(int);
    {
        length += sizeof(int);
        if (game->hero) {
            Role* role = (Role *)game->hero->data;
            length += HeroBufferSize((Hero *)role->target);
        }
    }
    {
        length += sizeof(int);
        if (game->sword) {
            Role* role = (Role *)game->sword->data;
            length += SwordBufferSize((Sword *)role->target);
        }
    }
    {
        length += sizeof(int);
        if (game->boss) {
            Role* role = (Role *)game->boss->data;
            length += BossBufferSize((Boss *)role->target);
        }
    }

    char* bb = (char *)malloc(sizeof(char) * length);
    *b = bb;

    *(int *)bb = game->status;
    bb += sizeof(int);

    *(int *)bb = namelen;
    bb += sizeof(int);
    memcpy(bb, game->name, namelen);
    bb += namelen;

    {
        if (game->hero) {
            *(int *)bb = ROLE_HERO;
            bb += sizeof(int);
            Role* role = (Role *)game->hero->data;
            bb += HeroPrint((Hero *)role->target, bb);
        } else {
            *(int *)bb = ROLE_NONE;
            bb += sizeof(int);
        }
    }
    {
        if (game->sword) {
            *(int *)bb = ROLE_SWORD;
            bb += sizeof(int);
            Role* role = (Role *)game->sword->data;
            bb += SwordPrint((Sword *)role->target, bb);
        } else {
            *(int *)bb = ROLE_NONE;
            bb += sizeof(int);
        }
    }
    {
        if (game->boss) {
            *(int *)bb = ROLE_BOSS;
            bb += sizeof(int);
            Role* role = (Role *)game->boss->data;
            bb += BossPrint((Boss *)role->target, bb);
        } else {
            *(int *)bb = ROLE_NONE;
            bb += sizeof(int);
        }
    }

    return length;
}

void GameReset(Game* game) {
    if (game->hero && game->sword && game->boss) {
        Role* hero = (Role *)game->hero->data;
        Role* boss = (Role *)game->boss->data;
        Role* sword = (Role *)game->sword->data;

        HeroReset(hero->target, boss->target);
        SwordReset(sword->target);
        HeroReset(boss->target, hero->target);
    }
}


